package com.cdn.openapi.utils;

/**
 * @author 蔡定努
 * @date 2024/08/12 10:38
 */

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cdn.openapi.consts.SignConst;
import com.cdn.openapi.entity.Merchant;
import com.cdn.openapi.exeception.SignException;
import com.cdn.openapi.mapper.MerchantMapper;
import com.cdn.openapi.service.IMerchantService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;

@Slf4j
@Component
public class AppUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;
    private static MerchantMapper merchantMapper;
    private static IMerchantService iMerchantService;

    /**
     * key:appId、value:appSecret
     */

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        AppUtil.applicationContext = applicationContext;
        AppUtil.iMerchantService = applicationContext.getBean(IMerchantService.class);
        AppUtil.merchantMapper = applicationContext.getBean(MerchantMapper.class);
    }


    /**
     * @author 蔡定努
     */
    public static boolean hasAuth(String appId, String apiAuth) {
        log.info("【服务端】验证权限：{}", apiAuth);
        Set<String> auth = merchantMapper.hasAuth(appId);
        if (auth.contains("*")) {
            return true;
        }
        return auth.contains(apiAuth);
    }


    /**
     * @param header
     * @param body
     * @return boolean
     */
    public static void serverVerify(Map<String, Object> header, Map<String, Object> body) {
        Optional.ofNullable(header.get(SignConst.TIMESTAMP)).orElseThrow(() -> new SignException("请求时间戳不能为空"));
        String timestamp = header.get(SignConst.TIMESTAMP).toString();

        // 请求时间有效期校验
        long currentTimeMillis = System.currentTimeMillis();
        if ((currentTimeMillis - Long.parseLong(timestamp)) / 1000 / 60 >= 5) {
            log.error("{}请求时间戳过期{}",header.get(SignConst.APP_ID),timestamp);
            throw new SignException("请求过期");
        }
        // 从header中获取相关信息，其中appSecret需要自己根据传过来的appId来获取
        Object appId = header.getOrDefault(SignConst.APP_ID, header.get("appid"));
        Optional.ofNullable(appId).orElseThrow(() -> new SignException("商户appId不能为空"));
        Merchant merchant = findMerchant(appId.toString());
        Optional.ofNullable(merchant).orElseThrow(() -> new SignException("商户不存在"));
        String sign = sign(merchant.getSecret(), header, body);
        if (!sign.equals(header.get("sign"))) {
            throw new SignException("签名错误");
        }
    }

    public static void main(String[] args) {

        String nonce = IdUtil.fastSimpleUUID();
        String timestampStr = System.currentTimeMillis() + "";
        String secretKey = "654321";
        // String sign = String.format("%s%s%s%s", secretKey, nonce, timestampStr,"");

        Map<String, Object> map = new HashMap<>();
        map.put(SignConst.NONCE, nonce);
        map.put(SignConst.TIMESTAMP, timestampStr);
        map.put(SignConst.APP_ID, "123456");


        HashMap<String, Object> objectObjectHashMap = new HashMap<>();
        objectObjectHashMap.put(SignConst.NONCE, nonce);

        String sign = sign(secretKey, map, objectObjectHashMap);
        System.out.println(sign);



        HttpRequest httpRequest = HttpRequest.get("http://localhost:807/test");
        httpRequest.header(SignConst.NONCE, nonce);
        httpRequest.header(SignConst.TIMESTAMP, timestampStr);
        httpRequest.header(SignConst.SIGN, sign);
        httpRequest.header(SignConst.APP_ID, "123456");
        HttpResponse execute = httpRequest.execute();

        System.out.println(execute.body());


    }


    /**
     * 签名
     *
     * @param secret
     * @param header
     * @return {@link String }
     */
    @SneakyThrows
    public static String sign(String secret, Map<String, Object> header, Map<String, Object> body) {
        HashMap<String, Object> map = new HashMap<>(body);
        map.put(SignConst.NONCE, header.get(SignConst.NONCE));
        map.put(SignConst.TIMESTAMP, header.get(SignConst.TIMESTAMP));
        Set<String> keySet = map.keySet();
        String[] keyArray = keySet.toArray(new String[0]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (sb.length() > 0) {
                sb.append("&");
            }
            if (map.get(k) != null) {
                sb.append(k).append("=").append(map.get(k));
            }
        }
        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
        mac.init(secretKey);
        byte[] hash = mac.doFinal(sb.toString().getBytes());
        return Base64.getEncoder().encodeToString(hash);
    }


    /**
     * 查询appSecret
     *
     * @param appId
     * @return {@link String }
     */
    public static Merchant findMerchant(String appId) {
        log.info("开始查询数据库" + appId + "对应的secret");
        LambdaQueryWrapper<Merchant> eq = Wrappers.lambdaQuery(Merchant.class)
                .eq(Merchant::getStatus, 1).eq(Merchant::getAppId, appId);
        return iMerchantService.getOne(eq);
    }

}